/*
 * tradutor.c
 *
 *  Created on: Jun 30, 2014
 *      Author: Hercules
 */
#include "tradutor.h"

int main(int argc, char *argv[]) {
	char* program_file_name;
	FILE *program_file, *translatedprogram_file;
	if (argc < 2) {
		printf("Faltam o nome do arquivo de entrada.\n");
		exit(1);
	} else {
		program_file_name = argv[1];
	}
	program_file = fopen(program_file_name, "r");
	translatedprogram_file = fopen("arquivo.s", "w");
	if (program_file == NULL) {
		printf("Erro ao abrir o arquivo: %s", program_file_name);
		exit(1);
	} else if (translatedprogram_file == NULL) {
		printf("Erro ao abrir o arquivo: %s", "arquivo.s");
		exit(1);
	}

	Token *token, *last_token;
	token = last_token = NULL;

	DataSection* dat = (DataSection*) malloc(sizeof(DataSection));
	DataSection* bss = (DataSection*) malloc(sizeof(DataSection));
	DataSection* text = (DataSection*) malloc(sizeof(DataSection));
	dat->next = bss->next = NULL;
	int shouldWriteIF = 0, WriteIf = 0, usedLerInteiro = 0,
			usedEscreverInteiro = 0;
	while ((token = getNextToken(program_file, last_token)) != NULL) {

		if (shouldWriteIF == 1) {
			WriteIf = 1;
			shouldWriteIF = 0;
		}
		if (token->token_delimiter == ':') {
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "%s:", token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "SECTION")) {
			getDataAndBssSection(program_file, translatedprogram_file, dat, bss,
					text);
		} else if (!strcmp(token->token_string, "IF")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "%%if %s = 1\n",
					token->token_string);
			insertInDataList(text, to_add);
			shouldWriteIF = 1;
		} else if (!strcmp(token->token_string, "ADD")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	add AX,[%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "SUB")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	sub AX,[%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "MULT")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	imul AX,[%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "DIV")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	idiv word [%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "JMP")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	jmp %s\n", token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "JMPN")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	cmp Ax,0\n");
			insertInDataList(text, to_add);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	jl %s\n", token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "JMPP")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	cmp Ax,0\n");
			insertInDataList(text, to_add);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	jg %s\n", token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "JMPZ")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	cmp Ax,0\n");
			insertInDataList(text, to_add);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	je %s\n", token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "COPY")) {
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	push EBX\n");
			insertInDataList(text, to_add);
			free(token);
			token = getNextToken(program_file, NULL);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	mov BX,[%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
			free(token);
			token = getNextToken(program_file, NULL);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	mov [%s],BX\n",
					token->token_string);
			insertInDataList(text, to_add);
			to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	pop EBX\n");
			insertInDataList(text, to_add);

		} else if (!strcmp(token->token_string, "LOAD")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	mov AX,[%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "STORE")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "	mov [%s],AX\n",
					token->token_string);
			insertInDataList(text, to_add);
		} else if (!strcmp(token->token_string, "INPUT")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine,
					"	push AX\n	call LerInteiro\n	pop word [%s]\n",
					token->token_string);
			insertInDataList(text, to_add);
			usedLerInteiro = 1;
		} else if (!strcmp(token->token_string, "OUTPUT")) {
			free(token);
			token = getNextToken(program_file, NULL);
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine,
					"	push word [%s]\n	call EscreverInteiro\n",
					token->token_string);
			insertInDataList(text, to_add);
			usedEscreverInteiro = 1;
		} else if (!strcmp(token->token_string, "STOP")) {
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine,
					"	mov EAX,1\n	mov EBX,0\n	int 0x80\n");
			insertInDataList(text, to_add);
		}
		if (WriteIf == 1) {
			WriteIf = 0;
			DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
			sprintf(to_add->dataSectionLine, "%%endif\n");
			insertInDataList(text, to_add);
		}
	}
	if (usedLerInteiro == 1) {
		DataSection* dat_to_add1 = (DataSection*) malloc(sizeof(DataSection));
		strcpy(dat_to_add1->dataSectionLine, "	LerInteiroMsg db \"INPUT:\"");
		insertInDataList(dat, dat_to_add1);

		DataSection* dat_to_add2 = (DataSection*) malloc(sizeof(DataSection));
		strcpy(dat_to_add2->dataSectionLine, "	LIMSGSIZE EQU $-LerInteiroMsg");
		insertInDataList(dat, dat_to_add2);

		DataSection* bss_to_add1 = (DataSection*) malloc(sizeof(DataSection));
		strcpy(bss_to_add1->dataSectionLine, "	charLido resb 1");
		insertInDataList(bss, bss_to_add1);
	}
	if (usedEscreverInteiro == 1) {
		DataSection* bss_to_add = (DataSection*) malloc(sizeof(DataSection));
		strcpy(bss_to_add->dataSectionLine, "	stringToWrite resb 12");
		insertInDataList(bss, bss_to_add);

		DataSection* dat_to_add = (DataSection*) malloc(sizeof(DataSection));
		strcpy(dat_to_add->dataSectionLine, "	outputstring db \"OUTPUT:\"");
		insertInDataList(dat, dat_to_add);

		DataSection* dat_to_add2 = (DataSection*) malloc(sizeof(DataSection));
		strcpy(dat_to_add2->dataSectionLine, "	EISIZE EQU $-outputstring");
		insertInDataList(dat, dat_to_add2);
	}
	writeDataAndBss(dat, bss, translatedprogram_file, usedEscreverInteiro,
			usedLerInteiro);
	writeText(text, translatedprogram_file);
	if (usedLerInteiro == 1) {
		writeLerInteiro(translatedprogram_file);
	}
	if (usedEscreverInteiro == 1) {
		writeEscreverInteiro(translatedprogram_file);
	}
	rewind(program_file);
	while((token = getNextToken(program_file,NULL))!=NULL){
		puts(token->token_string);
	}
	fclose(program_file);
	fclose(translatedprogram_file);
	return 0;
}
void writeText(DataSection* text, FILE* translatedprogram_file) {
	DataSection* node_to_iterate = NULL;

	node_to_iterate = text->next;
	while (node_to_iterate != NULL) {
		fprintf(translatedprogram_file, "%s", node_to_iterate->dataSectionLine);
		DataSection* toFree = node_to_iterate;
		node_to_iterate = node_to_iterate->next;
		free(toFree);
	}
}
void writeDataAndBss(DataSection *dat, DataSection* bss,
		FILE* translatedprogram_file, int usedEscreverInteiro,
		int usedLerInteiro) {
	DataSection* node_to_iterate = NULL;

	node_to_iterate = dat->next;
	fprintf(translatedprogram_file, "section .data\n");
	while (node_to_iterate != NULL) {
		fprintf(translatedprogram_file, "%s\n",
				node_to_iterate->dataSectionLine);
		DataSection* toFree = node_to_iterate;
		node_to_iterate = node_to_iterate->next;
		free(toFree);
	}

	node_to_iterate = bss->next;
	fprintf(translatedprogram_file, "section .bss\n");
	while (node_to_iterate != NULL) {
		fprintf(translatedprogram_file, "%s\n",
				node_to_iterate->dataSectionLine);
		DataSection* toFree = node_to_iterate;
		node_to_iterate = node_to_iterate->next;
		free(toFree);
	}

}
void writeLerInteiro(FILE* translatedprogram_file) {
	fprintf(translatedprogram_file, "LerInteiro: push EAX\n");
	fprintf(translatedprogram_file, "			push EBX\n");
	fprintf(translatedprogram_file, "			push ECX\n");
	fprintf(translatedprogram_file, "			push EDX\n");
	fprintf(translatedprogram_file, "			mov EAX,4\n");
	fprintf(translatedprogram_file, "			mov EBX,1\n");
	fprintf(translatedprogram_file, "			mov ECX,LerInteiroMsg\n");
	fprintf(translatedprogram_file, "			mov EDX,LIMSGSIZE\n");
	fprintf(translatedprogram_file, "			int 80h\n");
	fprintf(translatedprogram_file, "			mov EAX,0\n");
	fprintf(translatedprogram_file, "readLoop:	push EAX\n");
	fprintf(translatedprogram_file, "			mov EAX,3\n");
	fprintf(translatedprogram_file, "			mov EBX,0\n");
	fprintf(translatedprogram_file, "			mov ECX,charLido\n");
	fprintf(translatedprogram_file, "			mov EDX,1\n");
	fprintf(translatedprogram_file, "			int 80h\n");
	fprintf(translatedprogram_file, "			pop EAX\n");
	fprintf(translatedprogram_file, "			cmp byte [charLido],0x0a\n");
	fprintf(translatedprogram_file, "			je endLerInteiro\n");
	fprintf(translatedprogram_file, "			mov EBX,0\n");
	fprintf(translatedprogram_file, "			mov BL,[charLido]\n");
	fprintf(translatedprogram_file, "			sub BL,0x30\n");
	fprintf(translatedprogram_file, "			imul AX,10\n");
	fprintf(translatedprogram_file, "			add AX,BX\n");
	fprintf(translatedprogram_file, "			jmp readLoop\n");
	fprintf(translatedprogram_file, "endLerInteiro:\n");
	fprintf(translatedprogram_file, "			pop EDX\n");
	fprintf(translatedprogram_file, "			pop ECX\n");
	fprintf(translatedprogram_file, "			pop EBX\n");
	fprintf(translatedprogram_file, "			mov [ESP + 8],AX\n");
	fprintf(translatedprogram_file, "			pop EAX\n");
	fprintf(translatedprogram_file, "			ret\n");

}

void writeEscreverInteiro(FILE* translatedprogram_file) {
	fprintf(translatedprogram_file, "EscreverInteiro: push EBP\n");
	fprintf(translatedprogram_file, "			mov EBP,ESP\n");
	fprintf(translatedprogram_file, "			push EAX\n");
	fprintf(translatedprogram_file, "			push EBX\n");
	fprintf(translatedprogram_file, "			push ECX\n");
	fprintf(translatedprogram_file, "			push EDX\n");
	fprintf(translatedprogram_file, "			mov EAX,4\n");
	fprintf(translatedprogram_file, "			mov EBX,1\n");
	fprintf(translatedprogram_file, "			mov ECX,outputstring\n");
	fprintf(translatedprogram_file, "			mov EDX,EISIZE\n");
	fprintf(translatedprogram_file, "			int 80h\n");
	fprintf(translatedprogram_file, "			mov ECX,0\n");
	fprintf(translatedprogram_file, "			mov EAX,0\n");
	fprintf(translatedprogram_file, "			mov AX,word [EBP + 8]\n");
	fprintf(translatedprogram_file, "			mov EBX,10\n");
	fprintf(translatedprogram_file, "divloop:	mov EDX,0\n");
	fprintf(translatedprogram_file, "			div EBX\n");
	fprintf(translatedprogram_file, "			ADD DL,0x30\n");
	fprintf(translatedprogram_file, "			push DX\n");
	fprintf(translatedprogram_file, "			inc CX\n");
	fprintf(translatedprogram_file, "			cmp AX,0\n");
	fprintf(translatedprogram_file, "			jne divloop\n");
	fprintf(translatedprogram_file, "			mov EBX,0\n");
	fprintf(translatedprogram_file, "nextdigit:	pop AX\n");
	fprintf(translatedprogram_file, "			mov [stringToWrite + EBX],AL\n");
	fprintf(translatedprogram_file, "			inc BX\n");
	fprintf(translatedprogram_file, "			loop nextdigit\n");
	fprintf(translatedprogram_file, "			mov byte [stringToWrite + EBX],0x0d\n");
	fprintf(translatedprogram_file, "			inc BX\n");
	fprintf(translatedprogram_file, "			mov byte [stringToWrite + EBX],0x0a\n");
	fprintf(translatedprogram_file, "			mov EAX,4\n");
	fprintf(translatedprogram_file, "			mov EDX,12\n");
	fprintf(translatedprogram_file, "			mov EBX,1\n");
	fprintf(translatedprogram_file, "			mov ECX,stringToWrite\n");
	fprintf(translatedprogram_file, "			int 80h\n");
	fprintf(translatedprogram_file, "			pop EDX\n");
	fprintf(translatedprogram_file, "			pop ECX\n");
	fprintf(translatedprogram_file, "			pop EBX\n");
	fprintf(translatedprogram_file, "			pop EAX\n");
	fprintf(translatedprogram_file, "			pop EBP\n");
	fprintf(translatedprogram_file, "			ret 2\n");

}
void getDataAndBssSection(FILE* program_file, FILE* translatedprogram_file,
		DataSection* dat, DataSection *bss, DataSection *text) {
	Token* sectionToken = getNextToken(program_file, NULL);
	if (sectionToken == NULL) {
		puts("Erro: Section n�o possui argumento");
		exit(1);
	} else if (!strcmp(sectionToken->token_string, "TEXT")) {
		DataSection* to_add = (DataSection*) malloc(sizeof(DataSection));
		sprintf(to_add->dataSectionLine, "section .text\n");
		insertInDataList(text, to_add);
		to_add = (DataSection*) malloc(sizeof(DataSection));
		sprintf(to_add->dataSectionLine, "	global _start\n");
		insertInDataList(text, to_add);
		to_add = (DataSection*) malloc(sizeof(DataSection));
		sprintf(to_add->dataSectionLine, "_start: \n");
		insertInDataList(text, to_add);
	} else {
		free(sectionToken);
		while ((sectionToken = getNextToken(program_file, NULL)) != NULL) {
			if (!strcmp(sectionToken->token_string, "SECTION")) {
				fseek(program_file, strlen(sectionToken->token_string) + 1,
				SEEK_CUR);
				break;
			} else {
				DataSection* tempDat = (DataSection*) malloc(
						sizeof(DataSection));
				strcpy(tempDat->dataSectionLine, "\t");
				strcat(tempDat->dataSectionLine, sectionToken->token_string);
				strcat(tempDat->dataSectionLine, " ");
				free(sectionToken);
				sectionToken = getNextToken(program_file, NULL);
				if (sectionToken == NULL) {
					puts("Label para linha vazia na se��o de dados");
					exit(1);
				} else {
					if (!strcmp(sectionToken->token_string, "SPACE")) {
						strcat(tempDat->dataSectionLine, " resw ");
						if (sectionToken->token_delimiter == '\n') {
							strcat(tempDat->dataSectionLine, " 1");
						} else {
							free(sectionToken);
							sectionToken = getNextToken(program_file,
							NULL);
							strcat(tempDat->dataSectionLine,
									sectionToken->token_string);
						}
						free(sectionToken);
						insertInDataList(bss, tempDat);

					} else if (!strcmp(sectionToken->token_string, "CONST")) {
						strcat(tempDat->dataSectionLine, " dw ");
						free(sectionToken);
						sectionToken = getNextToken(program_file, NULL);
						strcat(tempDat->dataSectionLine,
								sectionToken->token_string);
						free(sectionToken);
						insertInDataList(dat, tempDat);
					} else if (!strcmp(sectionToken->token_string, "EQU")) {
						strcat(tempDat->dataSectionLine, " equ ");
						free(sectionToken);
						sectionToken = getNextToken(program_file, NULL);
						strcat(tempDat->dataSectionLine,
								sectionToken->token_string);
						free(sectionToken);
						insertInDataList(dat, tempDat);
					}
				}
			}
		}
	}
}
void insertInDataList(DataSection* list, DataSection* toAdd) {
	DataSection* node_to_iterate;
	node_to_iterate = list;
	while (node_to_iterate->next != NULL) {
		node_to_iterate = node_to_iterate->next;
	}
	node_to_iterate->next = toAdd;
	toAdd->next = NULL;
}
